/*
 *                               POK header
 *
 * The following file is a part of the POK project. Any modification should
 * be made according to the POK licence. You CANNOT use this file or a part
 * of a file for your own project.
 *
 * For more information on the POK licence, please see our LICENCE FILE
 *
 * Please follow the coding guidelines described in doc/CODING_GUIDELINES
 *
 *                                      Copyright (c) 2007-2021 POK team
 */

/* k_rem_pio2f.c -- float version of k_rem_pio2.c
 * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
 */

/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

#ifdef POK_NEEDS_LIBMATH

#include "math_private.h"
#include <libm.h>

/* In the float version, the input parameter x contains 8 bit
   integers, not 24 bit integers.  113 bit precision is not supported.  */

static const int init_jk[] = {4, 7, 9}; /* initial value for jk */

static const float PIo2[] = {
    1.5703125000e+00, /* 0x3fc90000 */
    4.5776367188e-04, /* 0x39f00000 */
    2.5987625122e-05, /* 0x37da0000 */
    7.5437128544e-08, /* 0x33a20000 */
    6.0026650317e-11, /* 0x2e840000 */
    7.3896444519e-13, /* 0x2b500000 */
    5.3845816694e-15, /* 0x27c20000 */
    5.6378512969e-18, /* 0x22d00000 */
    8.3009228831e-20, /* 0x1fc40000 */
    3.2756352257e-22, /* 0x1bc60000 */
    6.3331015649e-25, /* 0x17440000 */
};

static const float zero = 0.0, one = 1.0,
                   two8 = 2.5600000000e+02, /* 0x43800000 */
    twon8 = 3.9062500000e-03;               /* 0x3b800000 */

int __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec,
                       const int *ipio2) {
  int32_t jz, jx, jv, jp, jk, carry, n, iq[20], i, j, k, m, q0, ih;
  float z, fw, f[20], fq[20], q[20];

  /* initialize jk*/
  jk = init_jk[prec];
  jp = jk;

  /* determine jx,jv,q0, note that 3>q0 */
  jx = nx - 1;
  jv = (e0 - 3) / 8;
  if (jv < 0)
    jv = 0;
  q0 = e0 - 8 * (jv + 1);

  /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */
  j = jv - jx;
  m = jx + jk;
  for (i = 0; i <= m; i++, j++)
    f[i] = (j < 0) ? zero : (float)ipio2[j];

  /* compute q[0],q[1],...q[jk] */
  for (i = 0; i <= jk; i++) {
    for (j = 0, fw = 0.0; j <= jx; j++)
      fw += x[j] * f[jx + i - j];
    q[i] = fw;
  }

  jz = jk;
recompute:
  /* distill q[] into iq[] reversingly */
  for (i = 0, j = jz, z = q[jz]; j > 0; i++, j--) {
    fw = (float)((int32_t)(twon8 * z));
    iq[i] = (int32_t)(z - two8 * fw);
    z = q[j - 1] + fw;
  }

  /* compute n */
  z = scalbnf(z, q0);                         /* actual value of z */
  z -= (float)8.0 * floorf(z * (float)0.125); /* trim off integer >= 8 */
  n = (int32_t)z;
  z -= (float)n;
  ih = 0;
  if (q0 > 0) { /* need iq[jz-1] to determine n */
    i = (iq[jz - 1] >> (8 - q0));
    n += i;
    iq[jz - 1] -= i << (8 - q0);
    ih = iq[jz - 1] >> (7 - q0);
  } else if (q0 == 0)
    ih = iq[jz - 1] >> 8;
  else if (z >= (float)0.5)
    ih = 2;

  if (ih > 0) { /* q > 0.5 */
    n += 1;
    carry = 0;
    for (i = 0; i < jz; i++) { /* compute 1-q */
      j = iq[i];
      if (carry == 0) {
        if (j != 0) {
          carry = 1;
          iq[i] = 0x100 - j;
        }
      } else
        iq[i] = 0xff - j;
    }
    if (q0 > 0) { /* rare case: chance is 1 in 12 */
      switch (q0) {
      case 1:
        iq[jz - 1] &= 0x7f;
        break;
      case 2:
        iq[jz - 1] &= 0x3f;
        break;
      }
    }
    if (ih == 2) {
      z = one - z;
      if (carry != 0)
        z -= scalbnf(one, q0);
    }
  }

  /* check if recomputation is needed */
  if (z == zero) {
    j = 0;
    for (i = jz - 1; i >= jk; i--)
      j |= iq[i];
    if (j == 0) { /* need recomputation */
      for (k = 1; iq[jk - k] == 0; k++)
        ; /* k = no. of terms needed */

      for (i = jz + 1; i <= jz + k; i++) { /* add q[jz+1] to q[jz+k] */
        f[jx + i] = (float)ipio2[jv + i];
        for (j = 0, fw = 0.0; j <= jx; j++)
          fw += x[j] * f[jx + i - j];
        q[i] = fw;
      }
      jz += k;
      goto recompute;
    }
  }

  /* chop off zero terms */
  if (z == (float)0.0) {
    jz -= 1;
    q0 -= 8;
    while (iq[jz] == 0) {
      jz--;
      q0 -= 8;
    }
  } else { /* break z into 8-bit if necessary */
    z = scalbnf(z, -q0);
    if (z >= two8) {
      fw = (float)((int32_t)(twon8 * z));
      iq[jz] = (int32_t)(z - two8 * fw);
      jz += 1;
      q0 += 8;
      iq[jz] = (int32_t)fw;
    } else
      iq[jz] = (int32_t)z;
  }

  /* convert integer "bit" chunk to floating-point value */
  fw = scalbnf(one, q0);
  for (i = jz; i >= 0; i--) {
    q[i] = fw * (float)iq[i];
    fw *= twon8;
  }

  /* compute PIo2[0,...,jp]*q[jz,...,0] */
  for (i = jz; i >= 0; i--) {
    for (fw = 0.0, k = 0; k <= jp && k <= jz - i; k++)
      fw += PIo2[k] * q[i + k];
    fq[jz - i] = fw;
  }

  /* compress fq[] into y[] */
  switch (prec) {
  case 0:
    fw = 0.0;
    for (i = jz; i >= 0; i--)
      fw += fq[i];
    y[0] = (ih == 0) ? fw : -fw;
    break;
  case 1:
  case 2:
    fw = 0.0;
    for (i = jz; i >= 0; i--)
      fw += fq[i];
    y[0] = (ih == 0) ? fw : -fw;
    fw = fq[0] - fw;
    for (i = 1; i <= jz; i++)
      fw += fq[i];
    y[1] = (ih == 0) ? fw : -fw;
    break;
  case 3: /* painful */
    for (i = jz; i > 0; i--) {
      fw = fq[i - 1] + fq[i];
      fq[i] += fq[i - 1] - fw;
      fq[i - 1] = fw;
    }
    for (i = jz; i > 1; i--) {
      fw = fq[i - 1] + fq[i];
      fq[i] += fq[i - 1] - fw;
      fq[i - 1] = fw;
    }
    for (fw = 0.0, i = jz; i >= 2; i--)
      fw += fq[i];
    if (ih == 0) {
      y[0] = fq[0];
      y[1] = fq[1];
      y[2] = fw;
    } else {
      y[0] = -fq[0];
      y[1] = -fq[1];
      y[2] = -fw;
    }
  }
  return n & 7;
}

#endif
